DOCHER Mathieu

TP1 - Réggressions linéaires simple et multiple¶

A. Introduction¶

L'objectif de ce TP est de mettre en pratique la méthode de régression linéaire simple et multiple. Pour cela, nous allons utiliser un jeu de données contenant des informations sur le Covid-19 en France.

B. Préparation du jeu de données¶

Dans ce jeu de données, la variable à expliquer les weekly_icu_admissions, qui représente le nombre de personnes admises en soins intensifs chaque semaine.

In [30]:
import pandas as pd
import plotly.express as px
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.linear_model import LinearRegression
import statsmodels.api as sm
import plotly.io as pio
pio.renderers.default = "notebook" # pour ouvrir les graphiques dans le navigateur par défaut

# Récupération des données dans un dataframe
filename = "covidFranceData2022.csv"
df = pd.read_csv(filename)


# Remplacement des données manquantes (NA) par 0 (ici j'ai choisi de supprimer les lignes pour éviter de fausser les résultats, car les valeurs manquantes devrait être en milliers/millions)
df = df.dropna(how="any") # Suppression des lignes avec des valeurs manquantes
# df.fillna(0, inplace=True) # Remplacement des valeurs manquantes par 0
# -> Au final, on ne perd que 6 lignes sur 90.


# # Division du jeu de donnees en deux sous-ensembles (données cumulées et données journalières/hedomadaires)
# df = df[['date','positive_rate','tests_per_case','total_cases','total_deaths','total_tests','total_vaccinations','people_vaccinated','people_fully_vaccinated']]
# df_journalier = df[['date','positive_rate','tests_per_case','new_cases','new_deaths','icu_patients','hosp_patients','weekly_icu_admissions','weekly_hosp_admissions','new_tests','new_vaccinations']]
# # (finalement, je n'ai pas besoin de séparer en deux dataframes comme ça)

df = df.set_index('date')
# print(df.columns) # pour vérifier que la date ne fait pas partie des colonnes


# Visualisations de combinaisons de variables
# Nombre de cas en fonction du nombre de décès
fig = px.scatter(df, x="total_cases", y="total_deaths", color="positive_rate",hover_name=df.index, title="Nombre de décès en fonction du nombre de cas :")
fig.show()

# Nombre de décès hebdomadaires en fonction du nombre de cas hebdomadaires
fig = px.scatter(df, x="new_cases", y="new_deaths", color="positive_rate",hover_name=df.index, title="Nombre de décès en fonction des nouveaux cas :")
fig.show()

# Nombre de personnes en soins intensifs en fonction du nombre total de vaccinés
fig = px.scatter(df, x="people_vaccinated", y="icu_patients", color="positive_rate",hover_name=df.index, title="Nombre de personnes en soins intensifs en fonction du nombre total de vaccinés :")
fig.show()

# Nombre de personnes en soins intensifs par semaine par rapport au nombre de tests par semaine
fig = px.scatter(df, x="new_tests", y="weekly_icu_admissions", color="positive_rate",hover_name=df.index, title="Nombre de personnes en soins intensifs par rapport au nombre de tests par semaine :")
fig.show()

Ces quelques visualisations me semblent pertinentes pour analyser le nombre de personnes admises en soins intensifs et le nombre de décès en fonction du nombre de cas, de la vaccination ou du nombre de tests.

C. Régression linéaire¶

Une régression linéaire consiste à modéliser la relation entre une variable explicative, notée X, et une variable à expliquer, notée Y, par une droite. On utilise la régression linéaire simple lorsque l'on a une seule variable explicative et la régression linéaire multiple dès qu'il y en a plusieurs. La relation entre ces variables est modélisée par l'équation suivante :

$$Y = \beta_0 + \beta_1X_1 + \beta_2X_2 + ... + \beta_nX_n$$

(dans le cas d'une régression linéaire simple, il n'y a que $\beta_0$ et $\beta_1$)

On cherche à estimer les coefficients $\beta$ qui minimisent l'écart entre les valeurs réelles et les valeurs prédites par le modèle.

Pour commencer, on sépare le jeu de données en deux parties : une partie pour l'apprentissage du modèle et une partie pour le test.

In [31]:
df_train, df_test = train_test_split(df,test_size=0.2, random_state=21)

Voici la fonction retournant le modèle de régression linéaire simple :

In [32]:
def regression_lineaire_simple(df, variable_explicative, variable_a_predire):
    # Définition de la variable explicative et de la variable à expliquer 
    X = df[[variable_explicative]]
    Y = df[variable_a_predire]

    # Modélisation de la régression linéaire avec scikit-learn
    model = LinearRegression()
    model.fit(X, Y)
    # print(f"Variable à expliquer : {variable_a_predire}\nVariable explicative : {variable_explicative}\n\tcoef = {model.coef_[0]:.5f}\n\tintercept = {model.intercept_:.3f}\n\tR2 = {model.score(X, Y):.3f}")

    # Modélisation de la régression linéaire avec statsmodels
    X2 = sm.add_constant(X)
    model_sm = sm.OLS(Y, X2).fit()
    print(model_sm.summary())
    
    return model, X

Tout d'abord, nous allons mettre en place une régression linéaire simple avec le nombre d'admissions en hopital comme variable explicative :

In [33]:
model, X = regression_lineaire_simple(df_train, "weekly_hosp_admissions", "weekly_icu_admissions")

# Calcul de l'erreur quadratique moyenne
mse_training = mean_squared_error(df_test["weekly_icu_admissions"], model.predict(df_test[["weekly_hosp_admissions"]]))
print(f"{mse_training=}")

# Affichage du nuage de points et de la droite de régression
fig = px.scatter(df_train, x="weekly_hosp_admissions", y="weekly_icu_admissions", title="Nombre de personnes en soins intensifs par rapport au nombre de personnes hospitalisées :")
fig.add_scatter(x=X['weekly_hosp_admissions'], y=model.predict(X).flatten(), mode='lines', name='Régression linéaire', line=dict(color='limegreen', width=3), hoverinfo='none')
fig.update_layout(width=1000, height=500)
fig.show()
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.791
Model:                               OLS   Adj. R-squared:                  0.788
Method:                    Least Squares   F-statistic:                     245.7
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           9.20e-24
Time:                           14:25:20   Log-Likelihood:                -476.33
No. Observations:                     67   AIC:                             956.7
Df Residuals:                         65   BIC:                             961.1
Df Model:                              1                                         
Covariance Type:               nonrobust                                         
==========================================================================================
                             coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------------
const                     32.8645    100.543      0.327      0.745    -167.933     233.662
weekly_hosp_admissions     0.1165      0.007     15.673      0.000       0.102       0.131
==============================================================================
Omnibus:                       15.820   Durbin-Watson:                   1.448
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               19.084
Skew:                           1.297   Prob(JB):                     7.18e-05
Kurtosis:                       3.327   Cond. No.                     3.70e+04
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 3.7e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
mse_training=83523.14888581011

On fait ensuite la même manipulation avec les variables new deaths et new_tests :

In [34]:
model, X = regression_lineaire_simple(df_train, "new_deaths", "weekly_icu_admissions")

# Calcul de l'erreur quadratique moyenne
mse_training = mean_squared_error(df_test["weekly_icu_admissions"], model.predict(df_test[["new_deaths"]]))
print(f"{mse_training=}")

# Affichage du nuage de points et de la droite de régression
fig = px.scatter(df_train, x="new_deaths", y="weekly_icu_admissions", title="Nombre de personnes en soins intensifs par rapport au nombre de décès :")
fig.add_scatter(x=X['new_deaths'], y=model.predict(X).flatten(), mode='lines', name='Régression linéaire', line=dict(color='limegreen', width=3), hoverinfo='none')
fig.update_layout(width=1000, height=500)
fig.show()
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.091
Model:                               OLS   Adj. R-squared:                  0.077
Method:                    Least Squares   F-statistic:                     6.516
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):             0.0130
Time:                           14:25:20   Log-Likelihood:                -525.54
No. Observations:                     67   AIC:                             1055.
Df Residuals:                         65   BIC:                             1059.
Df Model:                              1                                         
Covariance Type:               nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const       1204.1324    138.842      8.673      0.000     926.845    1481.420
new_deaths     1.5558      0.609      2.553      0.013       0.339       2.773
==============================================================================
Omnibus:                      198.584   Durbin-Watson:                   1.461
Prob(Omnibus):                  0.000   Jarque-Bera (JB):                7.323
Skew:                           0.010   Prob(JB):                       0.0257
Kurtosis:                       1.380   Cond. No.                         413.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
mse_training=232801.57039895718
In [35]:
model, X = regression_lineaire_simple(df_train, "new_tests", "weekly_icu_admissions")

# Calcul de l'erreur quadratique moyenne
mse_training = mean_squared_error(df_test["weekly_icu_admissions"], model.predict(df_test[["new_tests"]]))
print(f"{mse_training=}")

# Affichage du nuage de points et de la droite de régression
fig = px.scatter(df_train, x="new_tests", y="weekly_icu_admissions", title="Nombre de personnes en soins intensifs par rapport au nombre de tests :")
fig.add_scatter(x=X['new_tests'], y=model.predict(X).flatten(), mode='lines', name='Régression linéaire', line=dict(color='limegreen', width=3), hoverinfo='none')
fig.update_layout(width=1000, height=500)
fig.show()
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.414
Model:                               OLS   Adj. R-squared:                  0.405
Method:                    Least Squares   F-statistic:                     45.91
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           4.31e-09
Time:                           14:25:20   Log-Likelihood:                -510.84
No. Observations:                     67   AIC:                             1026.
Df Residuals:                         65   BIC:                             1030.
Df Model:                              1                                         
Covariance Type:               nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        974.5104     98.933      9.850      0.000     776.928    1172.092
new_tests      0.0008      0.000      6.776      0.000       0.001       0.001
==============================================================================
Omnibus:                        6.171   Durbin-Watson:                   1.600
Prob(Omnibus):                  0.046   Jarque-Bera (JB):                3.412
Skew:                           0.339   Prob(JB):                        0.182
Kurtosis:                       2.127   Cond. No.                     1.38e+06
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.38e+06. This might indicate that there are
strong multicollinearity or other numerical problems.
mse_training=147961.73350965846

Il est possible de trouver numériquement les valeurs de $\beta_0$ et $\beta_1$ numériquement :

La pente $\beta_1 = \frac{\sum_{i=1}^{n} (X_i - \bar{X})(Y_i - \bar{Y})}{\sum_{i=1}^{n} (X_i - \bar{X})^2} = \frac{cov(X,Y)}{var(X)}$

Le biais $\beta_0 = \bar{Y} - \beta_1\bar{X}$

In [36]:
X = df_train['new_tests']
Y = df_train['weekly_icu_admissions']

# Calcul de cov(X,Y) et de var(X)
cov_XY = np.cov(X, Y)[0][1]
var_X = np.var(X)

# Calcul du coefficient directeur de la régression linéaire
coef = cov_XY / var_X

# Calcul de l'ordonnée à l'origine de la droite (intercept)
intercept = np.mean(Y) - coef * np.mean(X)

print(f"Les coefficients obtenus par le calcul pour X = 'new tests' sont :\n\t- {coef = :.5f},\n\t- {intercept = :.3f}")
Les coefficients obtenus par le calcul pour X = 'new tests' sont :
	- coef = 0.00079,
	- intercept = 966.551

On remarque que les valeurs sont très proches des valeurs obtenues par le modèle de régression linéaire simple de sklearn. La différence vient du fait que sklearn ajuste ses paramètres en entrainant le modèle après avoir calculé les valeurs de $\beta_0$ et $\beta_1$.

Le modèle qui semble être le plus pertinent est celui avec le nombre d'admissions en hopital comme variable explicative. En effet, ses valeurs de R² et de MSE sont respectevement plus élevé et plus faible que les autres modèles. Cela signifie que le modèle est plus précis et que les valeurs prédites sont plus proches des valeurs réelles.

Les variables new deaths et new_tests ne sont pas inutiles, mais elles ne sont pas aussi pertinentes que le nombre d'admissions en hopital. Mais comme nous sommes dans un cas de régression linéaire simple, il ne faut qu'une variable, donc elles sont inutiles ici.

D. Régression linéaire multiple¶

In [37]:
def regression_lineaire_multiple(df, variable_a_predire):
    # Définition de la variable explicative et de la variable à expliquer 
    X = df.drop(variable_a_predire, axis=1)
    Y = df[variable_a_predire]

    # Modélisation de la régression linéaire avec scikit-learn
    model= LinearRegression()
    model.fit(X, Y)
    # print(f"Variable à expliquer : {variable_a_predire}\n\tcoef = {model.coef_}\n\tintercept = {model.intercept_}\n\tR2 = {model.score(X, Y)}")

    # Modélisation de la régression linéaire avec statsmodels
    X2 = sm.add_constant(X)
    model_sm = sm.OLS(Y, X2).fit()
    print(model_sm.summary())
    
    return model, X

model1, X = regression_lineaire_multiple(df_train, "weekly_icu_admissions")
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.998
Model:                               OLS   Adj. R-squared:                  0.997
Method:                    Least Squares   F-statistic:                     1400.
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           3.51e-61
Time:                           14:25:20   Log-Likelihood:                -326.97
No. Observations:                     67   AIC:                             685.9
Df Residuals:                         51   BIC:                             721.2
Df Model:                             15                                         
Covariance Type:               nonrobust                                         
===========================================================================================
                              coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------
const                    3.088e+05    8.9e+04      3.468      0.001     1.3e+05    4.88e+05
total_cases                 0.0002      0.000      1.284      0.205   -8.84e-05       0.000
new_cases               -4.412e-05    8.5e-05     -0.519      0.606      -0.000       0.000
total_deaths               -0.2169      0.099     -2.198      0.033      -0.415      -0.019
new_deaths                  0.0686      0.058      1.175      0.245      -0.049       0.186
icu_patients                0.6307      0.226      2.792      0.007       0.177       1.084
hosp_patients              -0.0851      0.024     -3.527      0.001      -0.134      -0.037
weekly_hosp_admissions      0.1048      0.017      6.182      0.000       0.071       0.139
new_tests                 -4.6e-05      5e-05     -0.920      0.362      -0.000    5.43e-05
total_tests                 0.0001   4.51e-05      2.320      0.024    1.41e-05       0.000
positive_rate           -8568.5680   2925.176     -2.929      0.005   -1.44e+04   -2696.032
tests_per_case           -275.6044    117.679     -2.342      0.023    -511.854     -39.355
total_vaccinations       7.021e-05      0.000      0.484      0.631      -0.000       0.000
people_vaccinated          -0.0059      0.002     -2.542      0.014      -0.011      -0.001
people_fully_vaccinated     0.0001      0.001      0.082      0.935      -0.003       0.003
new_vaccinations         8.899e-05   9.34e-05      0.953      0.345   -9.84e-05       0.000
==============================================================================
Omnibus:                        0.787   Durbin-Watson:                   2.142
Prob(Omnibus):                  0.675   Jarque-Bera (JB):                0.302
Skew:                           0.111   Prob(JB):                        0.860
Kurtosis:                       3.243   Cond. No.                     5.69e+12
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 5.69e+12. This might indicate that there are
strong multicollinearity or other numerical problems.

Les variables statistiquement significatives sont celles qui ont un p-value inférieur à 0.05, il y a :

  • total_deaths
  • icu_patients
  • hosp_patients
  • weekly_hosp_admissions
  • total_tests
  • positive_rate
  • tests_per_case
  • people_vaccinated

On peut donc les utiliser pour faire une régression linéaire multiple et avoir un modèle plus précis.

In [38]:
# On crée un dataframe avec les variables explicatives qui semblent les plus pertinentes
df_train_2 = df_train[['total_deaths','icu_patients','hosp_patients','weekly_hosp_admissions','total_tests','positive_rate','tests_per_case','people_vaccinated','weekly_icu_admissions']]
model2, X = regression_lineaire_multiple(df_train_2, "weekly_icu_admissions")

# Affichage des prédictions 
df_test_2 = df_test[['total_deaths','icu_patients','hosp_patients','weekly_hosp_admissions','total_tests','positive_rate','tests_per_case','people_vaccinated','weekly_icu_admissions']]
predictions2 = model2.predict(df_test_2.drop("weekly_icu_admissions", axis=1))

fig = px.scatter(
    x=df_test.index, 
    y=predictions2, 
    title="Prédictions du nombre de personnes en soins intensifs :", 
    labels={'x': 'Date', 'y': 'Nombre de personnes en soins intensifs'}
)
fig.add_scatter(
    x=df_test.index, 
    y=predictions2, 
    mode='markers', 
    name='Prédictions', 
    line=dict(color='blue'), 
    hoverinfo='none'
)
fig.add_scatter(
    x=df_test.index, 
    y=df_test['weekly_icu_admissions'], 
    mode='markers',
    name='Valeurs réelles',
    marker=dict(color='red'),
    hoverinfo='none'
)
fig.update_layout(width=600, height=500)
fig.show()
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.997
Model:                               OLS   Adj. R-squared:                  0.996
Method:                    Least Squares   F-statistic:                     2294.
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           1.40e-69
Time:                           14:25:20   Log-Likelihood:                -335.77
No. Observations:                     67   AIC:                             689.5
Df Residuals:                         58   BIC:                             709.4
Df Model:                              8                                         
Covariance Type:               nonrobust                                         
==========================================================================================
                             coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------------------
const                   2.125e+05   4.36e+04      4.869      0.000    1.25e+05       3e+05
total_deaths              -0.0974      0.080     -1.222      0.227      -0.257       0.062
icu_patients               0.3736      0.195      1.912      0.061      -0.017       0.765
hosp_patients             -0.0349      0.013     -2.704      0.009      -0.061      -0.009
weekly_hosp_admissions     0.1228      0.014      8.688      0.000       0.094       0.151
total_tests             9.752e-05   3.02e-05      3.231      0.002    3.71e-05       0.000
positive_rate          -9150.0459   1998.305     -4.579      0.000   -1.32e+04   -5150.004
tests_per_case          -268.6855     78.132     -3.439      0.001    -425.085    -112.286
people_vaccinated         -0.0041      0.001     -5.458      0.000      -0.006      -0.003
==============================================================================
Omnibus:                        0.101   Durbin-Watson:                   1.932
Prob(Omnibus):                  0.951   Jarque-Bera (JB):                0.287
Skew:                          -0.042   Prob(JB):                        0.866
Kurtosis:                       2.691   Cond. No.                     2.23e+12
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 2.23e+12. This might indicate that there are
strong multicollinearity or other numerical problems.

Etrangement, le modèle où les variables moins pertinentes sont exclues est un peu moins précis (cf la valeur de R², R² ajusté et AIC). c'est peut-être dû à un mauvais tirage aléatoire des données d'entrainement. Ce nouveau modèle semble être un peu meilleur que le précédent malgré tout car il y a deux fois moins de variables pour une précision similaire.

In [39]:
# On crée un dataframe avec les variables explicatives qui semblent les plus pertinentes
df_train_3 = df_train[['positive_rate','tests_per_case','total_cases','total_deaths','total_tests','total_vaccinations','people_vaccinated','people_fully_vaccinated','weekly_icu_admissions']]
model3, X = regression_lineaire_multiple(df_train_3, "weekly_icu_admissions")

# Affichage des prédictions 
df_test_3 = df_test[['positive_rate','tests_per_case','total_cases','total_deaths','total_tests','total_vaccinations','people_vaccinated','people_fully_vaccinated','weekly_icu_admissions']]
predictions3 = model3.predict(df_test_3.drop("weekly_icu_admissions", axis=1))

# Pour le fun, prédiction avec toutes les variables (le premier modèle crée dans cette partie)
predictions1 = model1.predict(df_test.drop("weekly_icu_admissions", axis=1))

fig = px.scatter(
    x=df_test.index, 
    y=predictions1, 
    title="Prédictions du nombre de personnes en soins intensifs :", 
    labels={'x': 'Date', 'y': 'Nombre de personnes en soins intensifs'}
)
fig.add_scatter(
    x=df_test.index, 
    y=predictions1, 
    mode='markers', 
    name='Prédictions (avec toutes les variables)', 
    line=dict(color='orange'), 
    hoverinfo='none'
)
fig.add_scatter(
    x=df_test.index, 
    y=predictions2, 
    mode='markers', 
    name='Prédictions (avec toutes les variables pertinentes)', 
    line=dict(color='blue'), 
    hoverinfo='none'
)
fig.add_scatter(
    x=df_test.index, 
    y=predictions3, 
    mode='markers', 
    name='Prédictions (avec uniquement les variables cumulées)', 
    line=dict(color='green'), 
    hoverinfo='none'
)
fig.add_scatter(
    x=df_test.index, 
    y=df_test['weekly_icu_admissions'], 
    mode='markers', 
    name='Valeurs réelles', 
    marker=dict(color='red'), 
    hoverinfo='none'
)

fig.show()

# Calcul de l'erreur quadratique moyenne de chacun des modèles
mse_training1 = mean_squared_error(df_test["weekly_icu_admissions"], predictions1)
print(f"MSE avec toutes les variables:\t{mse_training1:.2f}")
mse_training2 = mean_squared_error(df_test["weekly_icu_admissions"], predictions2)
print(f"MSE avec variables pertinentes:\t{mse_training2:.2f}")
mse_training3 = mean_squared_error(df_test["weekly_icu_admissions"], predictions3)
print(f"MSE avec variables cumulées:\t{mse_training3:.2f}")
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.993
Model:                               OLS   Adj. R-squared:                  0.992
Method:                    Least Squares   F-statistic:                     1066.
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           5.53e-60
Time:                           14:25:20   Log-Likelihood:                -361.31
No. Observations:                     67   AIC:                             740.6
Df Residuals:                         58   BIC:                             760.5
Df Model:                              8                                         
Covariance Type:               nonrobust                                         
===========================================================================================
                              coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------
const                     4.05e+05   1.07e+05      3.802      0.000    1.92e+05    6.18e+05
positive_rate            -1.75e+04   3276.309     -5.342      0.000   -2.41e+04   -1.09e+04
tests_per_case           -654.6979    139.792     -4.683      0.000    -934.522    -374.873
total_cases                 0.0001   7.53e-05      1.428      0.159   -4.32e-05       0.000
total_deaths               -0.5734      0.033    -17.422      0.000      -0.639      -0.507
total_tests                 0.0003   2.52e-05     10.645      0.000       0.000       0.000
total_vaccinations      -1.648e-05      0.000     -0.098      0.923      -0.000       0.000
people_vaccinated          -0.0064      0.003     -2.402      0.020      -0.012      -0.001
people_fully_vaccinated    -0.0007      0.001     -0.512      0.610      -0.004       0.002
==============================================================================
Omnibus:                        5.878   Durbin-Watson:                   1.932
Prob(Omnibus):                  0.053   Jarque-Bera (JB):                5.183
Skew:                          -0.519   Prob(JB):                       0.0749
Kurtosis:                       3.883   Cond. No.                     4.35e+12
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 4.35e+12. This might indicate that there are
strong multicollinearity or other numerical problems.
MSE avec toutes les variables:	1386.03
MSE avec variables pertinentes:	1999.73
MSE avec variables cumulées:	9340.98

On remarque ici que le modèle de régression linéaire multiple le plus précis est celui utilisant toutes les variables du dataset (son erreur quadratique moyenne (et ses valeurs de R² et R²-ajusté) est plus faible que les autres modèles). Cela signifie que ce modèle est plus précis et que les valeurs prédites sont plus proches des valeurs réelles (comme on peut aussi le voir sur le graphique). Ce modèle est donc le meilleur pour prédire le nombre d'admissions en soins intensifs parmi les modèles testés.

De plus, On peut considérer que le modèle entrainé sur ces données est généralisable car il a été testé sur des données qu'il n'a jamais vues (les échantillons de test) et qu'il a donné des résultats plutôt satisfaisants. Pour être sûr que le modèle est bien généralisable, il faudrait tester le modèle sur un jeu de données plus grand et plus varié.

Extension 1 : Régression polynomiale¶

(comme cette partie n'a pas encore été traitée en cours, j'ai fait ce que j'ai pu à partir de recherches sur internet)

Une régression polynomiale est un cas particulier de régression linéaire où la relation entre les variables est modélisée par un polynôme. On peut donc utiliser une régression polynomiale pour modéliser des relations non-linéaires et anvoir un modèle plus précis.

Le polynôme de régression est de la forme :

$$P_n(x) = a_0 + a_1x + a_2x^2 + ... + a_{n-1}x^{n-1} + a_nx^n$$

Régression polynomiale simple¶

Voici la fonction retournant le modèle de régression polynomiale simple :

In [40]:
from sklearn.preprocessing import PolynomialFeatures

def regression_polynomiale_simple(df, variable_explicative, variable_a_predire, degre):
    # Définition de la variable explicative et de la variable à expliquer 
    X = df[[variable_explicative]]
    Y = df[variable_a_predire]

    # Modélisation de la régression polynomiale avec scikit-learn
    X_poly = PolynomialFeatures(degre).fit_transform(X)
    model = LinearRegression()
    model.fit(X_poly, Y)
    # print(f"Variable à expliquer : {variable_a_predire}\nVariable explicative : {variable_explicative}\n\tcoef = {model.coef_}\n\tintercept = {model.intercept_}\n\tR2 = {model.score(X_poly, Y)}")

    # Modélisation de la régression polynomiale avec statsmodels
    X2 = sm.add_constant(X_poly)
    model_sm = sm.OLS(Y, X2).fit()
    # print(model_sm.summary())

    return model, X

def add_polynomial_curve(fig, color, degre):

    # Création du modèle de régression polynomiale
    model, X = regression_polynomiale_simple(df_train, "weekly_hosp_admissions", "weekly_icu_admissions", degre)

    # Calcul de l'erreur quadratique moyenne et du R2
    mse_training = mean_squared_error(df_test["weekly_icu_admissions"], model.predict(PolynomialFeatures(degre).fit_transform(df_test[["weekly_hosp_admissions"]])))
    print(f"{degre=} ->\t{mse_training=:.2f}")
    R2 = model.score(PolynomialFeatures(degre).fit_transform(df_test[['weekly_hosp_admissions']]), df_test['weekly_icu_admissions'])
    print(f"\t|\t{R2=:.2f}")

    # Ajout de la courbe de régression polynomiale
    fig.add_scatter(
    x=np.sort(X['weekly_hosp_admissions']), 
    y=model.predict(PolynomialFeatures(degre).fit_transform(np.sort(X[['weekly_hosp_admissions']], axis=0))).flatten(), 
    mode='lines', 
    name=f"Régression polynomiale ({degre=})", 
    line=dict(color=color, width=1), 
    hoverinfo='none'
)
In [41]:
# Affichage du nuage de points et de la courbe de régression pour les degrés choisis
fig = px.scatter(df_train, x="weekly_hosp_admissions", y="weekly_icu_admissions", title="Nombre de personnes en soins intensifs par rapport au nombre de personnes hospitalisées :")

add_polynomial_curve(fig, 'red', 1)
add_polynomial_curve(fig, 'orange', 2)
add_polynomial_curve(fig, 'green', 5)
add_polynomial_curve(fig, 'purple', 9)

fig.update_layout(width=1000, height=500)
fig.show()
degre=1 ->	mse_training=83523.15
	|	R2=0.67
degre=2 ->	mse_training=41477.77
	|	R2=0.83
degre=5 ->	mse_training=34984.57
	|	R2=0.86
degre=9 ->	mse_training=32739.07
	|	R2=0.87

On remarque ici que le modèle de régression polynomiale simple le plus précis est celui utilisant le polynôme de plus grand degré. En effet, son erreur quadratique moyenne et sa valeur de R² sont plus faibles que les autres modèles. La différence entre le degré 1 et le degré 2 est très importante (division par 2 de la MSE) et plus le degré augmente, moins la précision augmente.

Régression polynomiale multiple¶

In [42]:
def regression_polynomiale_multiple(df, variable_a_predire, degre):
    # Définition de la variable explicative et de la variable à expliquer 
    X = df.drop(variable_a_predire, axis=1)
    Y = df[variable_a_predire]

    # Modélisation de la régression polynomiale avec scikit-learn
    X_poly = PolynomialFeatures(degre).fit_transform(X)
    model = LinearRegression()
    model.fit(X_poly, Y)

    # Modélisation de la régression linéaire avec statsmodels
    X2 = sm.add_constant(X)
    model_sm = sm.OLS(Y, X2).fit()
    print(model_sm.summary())

    return model, X
In [43]:
model1, X = regression_polynomiale_multiple(df_train, "weekly_icu_admissions", 2)

# On fait la prédiction sur le jeu de test
df_test_1 = df_test.drop("weekly_icu_admissions", axis=1)
predictions1 = model1.predict(PolynomialFeatures(2).fit_transform(df_test_1))

fig = px.scatter(
    x=df_test.index, 
    y=predictions2, 
    title="Prédictions du nombre de personnes en soins intensifs :", 
    labels={'x': 'Date', 'y': 'Nombre de personnes en soins intensifs'}
)
fig.add_scatter(
    x=df_test.index, 
    y=predictions2, 
    mode='markers', 
    name='Prédictions', 
    line=dict(color='blue'), 
    hoverinfo='none'
)
fig.add_scatter(
    x=df_test.index, 
    y=df_test['weekly_icu_admissions'], 
    mode='markers',
    name='Valeurs réelles',
    marker=dict(color='red'),
    hoverinfo='none'
)
fig.update_layout(width=600, height=500)
fig.show()
                              OLS Regression Results                             
=================================================================================
Dep. Variable:     weekly_icu_admissions   R-squared:                       0.998
Model:                               OLS   Adj. R-squared:                  0.997
Method:                    Least Squares   F-statistic:                     1400.
Date:                   Sat, 25 Jan 2025   Prob (F-statistic):           3.51e-61
Time:                           14:25:20   Log-Likelihood:                -326.97
No. Observations:                     67   AIC:                             685.9
Df Residuals:                         51   BIC:                             721.2
Df Model:                             15                                         
Covariance Type:               nonrobust                                         
===========================================================================================
                              coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------------------
const                    3.088e+05    8.9e+04      3.468      0.001     1.3e+05    4.88e+05
total_cases                 0.0002      0.000      1.284      0.205   -8.84e-05       0.000
new_cases               -4.412e-05    8.5e-05     -0.519      0.606      -0.000       0.000
total_deaths               -0.2169      0.099     -2.198      0.033      -0.415      -0.019
new_deaths                  0.0686      0.058      1.175      0.245      -0.049       0.186
icu_patients                0.6307      0.226      2.792      0.007       0.177       1.084
hosp_patients              -0.0851      0.024     -3.527      0.001      -0.134      -0.037
weekly_hosp_admissions      0.1048      0.017      6.182      0.000       0.071       0.139
new_tests                 -4.6e-05      5e-05     -0.920      0.362      -0.000    5.43e-05
total_tests                 0.0001   4.51e-05      2.320      0.024    1.41e-05       0.000
positive_rate           -8568.5680   2925.176     -2.929      0.005   -1.44e+04   -2696.032
tests_per_case           -275.6044    117.679     -2.342      0.023    -511.854     -39.355
total_vaccinations       7.021e-05      0.000      0.484      0.631      -0.000       0.000
people_vaccinated          -0.0059      0.002     -2.542      0.014      -0.011      -0.001
people_fully_vaccinated     0.0001      0.001      0.082      0.935      -0.003       0.003
new_vaccinations         8.899e-05   9.34e-05      0.953      0.345   -9.84e-05       0.000
==============================================================================
Omnibus:                        0.787   Durbin-Watson:                   2.142
Prob(Omnibus):                  0.675   Jarque-Bera (JB):                0.302
Skew:                           0.111   Prob(JB):                        0.860
Kurtosis:                       3.243   Cond. No.                     5.69e+12
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 5.69e+12. This might indicate that there are
strong multicollinearity or other numerical problems.

En observant le résultat, on voit que la prédiction obtenue à partie de la régression polynomiale multiple est très proche de celui de la régression linéaire multiple. Cela signifie soit que les deux modèles sont équivalents en terme de précision, soit qu'il y a une erreur dans le code (ce qui est probable 😅).

Conclusion¶

Dans ce TP, nous avons mis en place des modèles de régression linéaire simple et multiple pour prédire le nombre d'admissions en soins intensifs en fonction de différentes variables. Nous avons vu que le modèle le plus précis est celui utilisant toutes les variables du dataset. Nous avons aussi essayé de mettre en place des modèles de régression polynomiale simple et multiple pour voir si un modèle plus précis pouvait être obtenu mais les résultats ne semblent trop proches de ceux des modèles de régression linéaire, c'est étrange...